#pragma once

#include "RHI.h"

#include "colorvalue.h"

#include "hash.h"

namespace RenderSystem
{
	// Forward declarations.
	template<ERHIResourceTypes ResourceType>
	class TFDynamicRHIResourceReference;

	/** This type is just used to give a resource a distinct type when it's passed around outside of this reference counted wrapper. */
	template<ERHIResourceTypes ResourceType>
	class TFDynamicRHIResource
	{
	};
	//
	// Statically bound RHI resource reference type definitions for the dynamically bound RHI.
	//
#define DEFINE_FDynamicRHI_REFERENCE_TYPE(Type,ParentType) \
	template<> class TFDynamicRHIResource<RRT_##Type> : public TFDynamicRHIResource<RRT_##ParentType> {}; \
	typedef TFDynamicRHIResource<RRT_##Type>*			F##Type##RHIParamRef; \
	typedef TFDynamicRHIResourceReference<RRT_##Type>	F##Type##RHIRef;

	ENUM_RHI_RESOURCE_TYPES(DEFINE_FDynamicRHI_REFERENCE_TYPE);

#undef DEFINE_FDynamicRHI_REFERENCE_TYPE

	/** specifies a texture and region to copy */
	struct FCopyTextureRegion2D
	{
		/** source texture to lock and copy from */
		FTexture2DRHIParamRef SrcTexture;
		/** horizontal texel offset for copy region */
		INT OffsetX;
		/** vertical texel offset for copy region */
		INT OffsetY;
		/** horizontal texel size for copy region */
		INT SizeX;
		/** vertical texel size for copy region */
		INT SizeY;	
		/** Starting mip index. This is treated as the base level (default is 0) */
		INT FirstMipIdx;
		/** constructor */
		FCopyTextureRegion2D(FTexture2DRHIParamRef InSrcTexture,INT InOffsetX=-1,INT InOffsetY=-1,INT InSizeX=-1,INT InSizeY=-1,INT InFirstMipIdx=0)
			:	SrcTexture(InSrcTexture)
			,	OffsetX(InOffsetX)
			,	OffsetY(InOffsetY)
			,	SizeX(InSizeX)
			,	SizeY(InSizeY)
			,	FirstMipIdx(InFirstMipIdx)
		{}
	};

	typedef std::vector<FCopyTextureRegion2D>					CopyTexRegion2DS;
	typedef std::vector<FCopyTextureRegion2D>::iterator			CopyTexRegion2DSIterator;
	typedef std::vector<FCopyTextureRegion2D>::const_iterator	CopyTexRegion2DSConstIterator;

	//dynamic: XXXX_RHI()

	class FDynamicRHI
	{
	public:
		virtual ~FDynamicRHI(void);

	#define DEFINE_RHIMETHOD(Type, Name, ParameterTypesAndNames, ParameterNames, ReturnStatement, DefaultImplementation) virtual Type Name ParameterTypesAndNames = 0
	#include "RHIMethods.h"
	#undef DEFINE_RHIMETHOD

		// Reference counting API for the different resource types.
	#define DEFINE_FDynamicRHI_REFCOUNTING_FORTYPE(Type,ParentType) \
		virtual void AddResourceRef(TFDynamicRHIResource<RRT_##Type>* Reference) = 0; \
		virtual void RemoveResourceRef(TFDynamicRHIResource<RRT_##Type>* Reference) = 0; \
		virtual DWORD GetRefCount(TFDynamicRHIResource<RRT_##Type>* Reference) = 0;
	ENUM_RHI_RESOURCE_TYPES(DEFINE_FDynamicRHI_REFCOUNTING_FORTYPE);
	#undef DEFINE_FDynamicRHI_REFCOUNTING_FORTYPE

	};

	extern FDynamicRHI* GFDynamicRHI;

	/**
	* A reference to a dynamically bound RHI resource.
	* When using the dynamically bound RHI, the reference counting goes through a dynamically bound interface in GFDynamicRHI.
	* @param ResourceType - The type of resource the reference may point to.
	*/
	template<ERHIResourceTypes ResourceType>
	class TFDynamicRHIResourceReference
	{
	public:

		typedef TFDynamicRHIResource<ResourceType>* ReferenceType;

		/** Default constructor. */
		TFDynamicRHIResourceReference():
		Reference(NULL)
		{}

		/** Initialization constructor. */
		TFDynamicRHIResourceReference(ReferenceType InReference)
		{
			Reference = InReference;
			if(Reference)
			{
				GFDynamicRHI->AddResourceRef(Reference);
			}
		}

		/** Copy constructor. */
		TFDynamicRHIResourceReference(const TFDynamicRHIResourceReference& Copy)
		{
			Reference = Copy.Reference;
			if(Reference)
			{
				GFDynamicRHI->AddResourceRef(Reference);
			}
		}

		/** Destructor. */
		~TFDynamicRHIResourceReference()
		{
			if(Reference)
			{
				GFDynamicRHI->RemoveResourceRef(Reference);
			}
		}

		/** Assignment operator. */
		TFDynamicRHIResourceReference& operator=(ReferenceType InReference)
		{
			ReferenceType OldReference = Reference;
			if(InReference)
			{
				GFDynamicRHI->AddResourceRef(InReference);
			}
			Reference = InReference;
			if(OldReference)
			{
				GFDynamicRHI->RemoveResourceRef(OldReference);
			}
			return *this;
		}

		/** Assignment operator. */
		TFDynamicRHIResourceReference& operator=(const TFDynamicRHIResourceReference& InPtr)
		{
			return *this = InPtr.Reference;
		}

		/** Equality operator. */
		bool operator==(const TFDynamicRHIResourceReference& Other) const
		{
			return Reference == Other.Reference;
		}

		/** Dereference operator. */
		void* operator->() const
		{
			return Reference;
		}

		/** Dereference operator. */
		operator ReferenceType() const
		{
			return Reference;
		}

		/** Type hashing. */
		friend DWORD GetTypeHash(ReferenceType Reference)
		{
			return Common::PointerHash(Reference);
		}

		// RHI reference interface.
		friend bool IsValidRef(const TFDynamicRHIResourceReference& Ref)
		{
			return Ref.Reference != NULL;
		}

		void SafeRelease()
		{
			*this = NULL;
		}
		DWORD GetRefCount()
		{
			if(Reference)
			{
				return GFDynamicRHI->GetRefCount(Reference);
			}
			else
			{
				return 0;
			}
		}

	private:
		ReferenceType Reference;
	};

	//static: RHI_XXXX_RHI()

	// Implement the statically bound RHI methods to simply call the dynamic RHI.
#define DEFINE_RHIMETHOD(Type,Name,ParameterTypesAndNames,ParameterNames,ReturnStatement,NullImplementation) \
	FORCEINLINE Type RHI##Name ParameterTypesAndNames \
	{ \
	assert(GFDynamicRHI); \
	ReturnStatement GFDynamicRHI->Name ParameterNames; \
	}
#include "RHIMethods.h"
#undef DEFINE_RHIMETHOD

}